//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

/// A list of `ChannelHandler`s that handle or intercept inbound events and outbound operations of a
/// `Channel`. `ChannelPipeline` implements an advanced form of the Intercepting Filter pattern
/// to give a user full control over how an event is handled and how the `ChannelHandler`s in a pipeline
/// interact with each other.
///
/// # Creation of a pipeline
///
/// Each `Channel` has its own `ChannelPipeline` and it is created automatically when a new `Channel` is created.
///
/// # How an event flows in a pipeline
///
/// The following diagram describes how I/O events are typically processed by `ChannelHandler`s in a `ChannelPipeline`.
/// An I/O event is handled by either a `ChannelInboundHandler` or a `ChannelOutboundHandler`
/// and is forwarded to the next handler in the `ChannelPipeline` by calling the event propagation methods defined in
/// `ChannelHandlerContext`, such as `ChannelHandlerContext.fireChannelRead` and
/// `ChannelHandlerContext.write`.
///
/// ```
///                                                    I/O Request
///                                                    via `Channel` or
///                                                    `ChannelHandlerContext`
///                                                      |
///  +---------------------------------------------------+---------------+
///  |                           ChannelPipeline         |               |
///  |                                TAIL              \|/              |
///  |    +---------------------+            +-----------+----------+    |
///  |    | Inbound Handler  N  |            | Outbound Handler  1  |    |
///  |    +----------+----------+            +-----------+----------+    |
///  |              /|\                                  |               |
///  |               |                                  \|/              |
///  |    +----------+----------+            +-----------+----------+    |
///  |    | Inbound Handler N-1 |            | Outbound Handler  2  |    |
///  |    +----------+----------+            +-----------+----------+    |
///  |              /|\                                  .               |
///  |               .                                   .               |
///  | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|
///  |        [ method call]                       [method call]         |
///  |               .                                   .               |
///  |               .                                  \|/              |
///  |    +----------+----------+            +-----------+----------+    |
///  |    | Inbound Handler  2  |            | Outbound Handler M-1 |    |
///  |    +----------+----------+            +-----------+----------+    |
///  |              /|\                                  |               |
///  |               |                                  \|/              |
///  |    +----------+----------+            +-----------+----------+    |
///  |    | Inbound Handler  1  |            | Outbound Handler  M  |    |
///  |    +----------+----------+            +-----------+----------+    |
///  |              /|\             HEAD                 |               |
///  +---------------+-----------------------------------+---------------+
///                  |                                  \|/
///  +---------------+-----------------------------------+---------------+
///  |               |                                   |               |
///  |       [ Socket.read ]                    [ Socket.write ]         |
///  |                                                                   |
///  |  SwiftNIO Internal I/O Threads (Transport Implementation)         |
///  +-------------------------------------------------------------------+
/// ```
///
/// An inbound event is handled by the inbound handlers in the head-to-tail direction as shown on the left side of the
/// diagram. An inbound handler usually handles the inbound data generated by the I/O thread on the bottom of the
/// diagram. The inbound data is often read from a remote peer via the actual input operation such as
/// `Socket.read`. If an inbound event goes beyond the tail inbound handler, it is discarded
/// silently, or logged if it needs your attention.
///
/// An outbound event is handled by the outbound handlers in the tail-to-head direction as shown on the right side of the
/// diagram. An outbound handler usually generates or transforms the outbound traffic such as write requests.
/// If an outbound event goes beyond the head outbound handler, it is handled by an I/O thread associated with the
/// `Channel`. The I/O thread often performs the actual output operation such as `Socket.write`.
///
///
/// For example, let us assume that we created the following pipeline:
///
/// ```
/// ChannelPipeline p = ...
/// let future = p.add(name: "1", handler: InboundHandlerA()).flatMap {
///   p.add(name: "2", handler: InboundHandlerB())
/// }.flatMap {
///   p.add(name: "3", handler: OutboundHandlerA())
/// }.flatMap {
///   p.add(name: "4", handler: OutboundHandlerB())
/// }.flatMap {
///   p.add(name: "5", handler: InboundOutboundHandlerX())
/// }
/// // Handle the future as well ....
/// ```
///
/// In the example above, a class whose name starts with `Inbound` is an inbound handler.
/// A class whose name starts with `Outbound` is an outbound handler.
///
/// In the given example configuration, the handler evaluation order is 1, 2, 3, 4, 5 when an event goes inbound.
/// When an event goes outbound, the order is 5, 4, 3, 2, 1.  On top of this principle, `ChannelPipeline` skips
/// the evaluation of certain handlers to shorten the stack depth:
///
/// - 3 and 4 don't implement `ChannelInboundHandler`, and therefore the actual evaluation order of an inbound event will be: 1, 2, and 5.
/// - 1 and 2 don't implement `ChannelOutboundHandler`, and therefore the actual evaluation order of a outbound event will be: 5, 4, and 3.
/// - If 5 implements both `ChannelInboundHandler` and `ChannelOutboundHandler`, the evaluation order of an inbound and a outbound event could be 125 and 543 respectively.
///
/// Note:   Handlers may choose not to propagate messages down the pipeline immediately.  For example a handler may need to wait
/// for additional data before sending a protocol event to the next handler in the pipeline.  Due to this you can't assume that later handlers
/// in the pipeline will receive the same number of events as were sent, or that events of different types will arrive in the same order.
/// For example - a user event could overtake a data event if a handler is aggregating data events before propagating but immediately
/// propagating user events.
///
/// # Forwarding an event to the next handler
///
/// As you might noticed in the diagram above, a handler has to invoke the event propagation methods in
/// `ChannelHandlerContext` to forward an event to its next handler.
/// Those methods include:
///
/// - Inbound event propagation methods defined in `ChannelInboundInvoker`
/// - Outbound event propagation methods defined in `ChannelOutboundInvoker`.
///
/// # Building a pipeline
///
/// A user is supposed to have one or more `ChannelHandler`s in a `ChannelPipeline` to receive I/O events (e.g. read) and
/// to request I/O operations (e.g. write and close).  For example, a typical server will have the following handlers
/// in each channel's pipeline, but your mileage may vary depending on the complexity and characteristics of the
/// protocol and business logic:
///
/// - Protocol Decoder - translates binary data (e.g. `ByteBuffer`) into a struct / class
/// - Protocol Encoder - translates a struct / class into binary data (e.g. `ByteBuffer`)
/// - Business Logic Handler - performs the actual business logic (e.g. database access)
///
/// # Thread safety
///
/// A `ChannelHandler` can be added or removed at any time because a `ChannelPipeline` is thread safe.
public final class ChannelPipeline: ChannelInvoker {
    private var head: Optional<ChannelHandlerContext>
    private var tail: Optional<ChannelHandlerContext>

    private var idx: Int = 0
    internal private(set) var destroyed: Bool = false

    /// The `EventLoop` that is used by the underlying `Channel`.
    public let eventLoop: EventLoop

    /// The `Channel` that this `ChannelPipeline` belongs to.
    ///
    /// - note: This will be nil after the channel has closed
    private var _channel: Optional<Channel>

    /// The `Channel` that this `ChannelPipeline` belongs to.
    internal var channel: Channel {
        self.eventLoop.assertInEventLoop()
        assert(self._channel != nil || self.destroyed)
        return self._channel ?? DeadChannel(pipeline: self)
    }

    /// Add a `ChannelHandler` to the `ChannelPipeline`.
    ///
    /// - parameters:
    ///     - name: the name to use for the `ChannelHandler` when it's added. If none is specified it will generate a name.
    ///     - handler: the `ChannelHandler` to add
    ///     - position: The position in the `ChannelPipeline` to add `handler`. Defaults to `.last`.
    /// - returns: the `EventLoopFuture` which will be notified once the `ChannelHandler` was added.
    public func addHandler(_ handler: ChannelHandler,
                           name: String? = nil,
                           position: ChannelPipeline.Position = .last) -> EventLoopFuture<Void> {
        let future: EventLoopFuture<Void>

        if self.eventLoop.inEventLoop {
            future = self.eventLoop.makeCompletedFuture(self.addHandlerSync(handler, name: name, position: position))
        } else {
            future = self.eventLoop.submit {
                try self.addHandlerSync(handler, name: name, position: position).get()
            }
        }

        return future
    }

    /// Synchronously add a `ChannelHandler` to the `ChannelPipeline`.
    ///
    /// May only be called from on the event loop.
    ///
    /// - parameters:
    ///     - handler: the `ChannelHandler` to add
    ///     - name: the name to use for the `ChannelHandler` when it's added. If none is specified a name will be generated.
    ///     - position: The position in the `ChannelPipeline` to add `handler`. Defaults to `.last`.
    /// - returns: the result of adding this handler - either success or failure with an error code if this could not be completed.
    fileprivate func addHandlerSync(_ handler: ChannelHandler,
                                    name: String? = nil,
                                    position: ChannelPipeline.Position = .last) -> Result<Void, Error> {
        self.eventLoop.assertInEventLoop()

        if self.destroyed {
            return .failure(ChannelError.ioOnClosedChannel)
        }

        switch position {
        case .first:
            return self.add0(name: name,
                             handler: handler,
                             relativeContext: head!,
                             operation: self.add0(context:after:))
        case .last:
            return self.add0(name: name,
                             handler: handler,
                             relativeContext: tail!,
                             operation: self.add0(context:before:))
        case .before(let beforeHandler):
            return self.add0(name: name,
                             handler: handler,
                             relativeHandler: beforeHandler,
                             operation: self.add0(context:before:))
        case .after(let afterHandler):
            return self.add0(name: name,
                             handler: handler,
                             relativeHandler: afterHandler,
                             operation: self.add0(context:after:))
        }
    }

    /// Synchronously add a `ChannelHandler` to the pipeline, relative to another `ChannelHandler`,
    /// where the insertion is done by a specific operation.
    ///
    /// May only be called from on the event loop.
    ///
    /// This will search the pipeline for `relativeHandler` and, if it cannot find it, will fail
    /// `promise` with `ChannelPipelineError.notFound`.
    ///
    /// - parameters:
    ///     - name: The name to use for the `ChannelHandler` when its added. If none is specified, a name will be
    ///         automatically generated.
    ///     - handler: The `ChannelHandler` to add.
    ///     - relativeHandler: The `ChannelHandler` already in the `ChannelPipeline` that `handler` will be
    ///         inserted relative to.
    ///     - operation: A callback that will insert `handler` relative to `relativeHandler`.
    /// - returns: the result of adding this handler - either success or failure with an error code if this could not be completed.
    private func add0(name: String?,
                      handler: ChannelHandler,
                      relativeHandler: ChannelHandler,
                      operation: (ChannelHandlerContext, ChannelHandlerContext) -> Void) -> Result<Void, Error> {
        self.eventLoop.assertInEventLoop()
        if self.destroyed {
            return .failure(ChannelError.ioOnClosedChannel)
        }

        guard let context = self.contextForPredicate0({ $0.handler === relativeHandler }) else {
            return .failure(ChannelPipelineError.notFound)
        }

        return self.add0(name: name, handler: handler, relativeContext: context, operation: operation)
    }

    /// Synchronously add a `ChannelHandler` to the pipeline, relative to a `ChannelHandlerContext`,
    /// where the insertion is done by a specific operation.
    ///
    /// May only be called from on the event loop.
    ///
    /// This method is more efficient than the one that takes a `relativeHandler` as it does not need to
    /// search the pipeline for the insertion point. It should be used whenever possible.
    ///
    /// - parameters:
    ///     - name: The name to use for the `ChannelHandler` when its added. If none is specified, a name will be
    ///         automatically generated.
    ///     - handler: The `ChannelHandler` to add.
    ///     - relativeContext: The `ChannelHandlerContext` already in the `ChannelPipeline` that `handler` will be
    ///         inserted relative to.
    ///     - operation: A callback that will insert `handler` relative to `relativeHandler`.
    /// - returns: the result of adding this handler - either success or failure with an error code if this could not be completed.
    private func add0(name: String?,
                      handler: ChannelHandler,
                      relativeContext: ChannelHandlerContext,
                      operation: (ChannelHandlerContext, ChannelHandlerContext) -> Void) -> Result<Void, Error> {
        self.eventLoop.assertInEventLoop()

        if self.destroyed {
            return .failure(ChannelError.ioOnClosedChannel)
        }

        let context = ChannelHandlerContext(name: name ?? nextName(), handler: handler, pipeline: self)
        operation(context, relativeContext)

        context.invokeHandlerAdded()
        return .success(())
    }

    /// Synchronously add a single new `ChannelHandlerContext` after one that currently exists in the
    /// pipeline.
    ///
    /// Must be called from within the event loop thread, as it synchronously manipulates the
    /// `ChannelHandlerContext`s on the `ChannelPipeline`.
    ///
    /// - parameters:
    ///     - new: The `ChannelHandlerContext` to add to the pipeline.
    ///     - existing: The `ChannelHandlerContext` that `new` will be added after.
    private func add0(context new: ChannelHandlerContext, after existing: ChannelHandlerContext) {
        self.eventLoop.assertInEventLoop()

        let next = existing.next
        new.prev = existing
        new.next = next
        existing.next = new
        next?.prev = new
    }

    /// Synchronously add a single new `ChannelHandlerContext` before one that currently exists in the
    /// pipeline.
    ///
    /// Must be called from within the event loop thread, as it synchronously manipulates the
    /// `ChannelHandlerContext`s on the `ChannelPipeline`.
    ///
    /// - parameters:
    ///     - new: The `ChannelHandlerContext` to add to the pipeline.
    ///     - existing: The `ChannelHandlerContext` that `new` will be added before.
    private func add0(context new: ChannelHandlerContext, before existing: ChannelHandlerContext) {
        self.eventLoop.assertInEventLoop()

        let prev = existing.prev
        new.prev = prev
        new.next = existing
        existing.prev = new
        prev?.next = new
    }

    /// Remove a `ChannelHandler` from the `ChannelPipeline`.
    ///
    /// - parameters:
    ///     - handler: the `ChannelHandler` to remove.
    /// - returns: the `EventLoopFuture` which will be notified once the `ChannelHandler` was removed.
    public func removeHandler(_ handler: RemovableChannelHandler) -> EventLoopFuture<Void> {
        let promise = self.eventLoop.makePromise(of: Void.self)
        self.removeHandler(handler, promise: promise)
        return promise.futureResult
    }

    /// Remove a `ChannelHandler` from the `ChannelPipeline`.
    ///
    /// - parameters:
    ///     - name: the name that was used to add the `ChannelHandler` to the `ChannelPipeline` before.
    /// - returns: the `EventLoopFuture` which will be notified once the `ChannelHandler` was removed.
    public func removeHandler(name: String) -> EventLoopFuture<Void> {
        let promise = self.eventLoop.makePromise(of: Void.self)
        self.removeHandler(name: name, promise: promise)
        return promise.futureResult
    }

    /// Remove a `ChannelHandler` from the `ChannelPipeline`.
    ///
    /// - parameters:
    ///     - context: the `ChannelHandlerContext` that belongs to `ChannelHandler` that should be removed.
    /// - returns: the `EventLoopFuture` which will be notified once the `ChannelHandler` was removed.
    public func removeHandler(context: ChannelHandlerContext) -> EventLoopFuture<Void> {
        let promise = self.eventLoop.makePromise(of: Void.self)
        self.removeHandler(context: context, promise: promise)
        return promise.futureResult
    }

    /// Remove a `ChannelHandler` from the `ChannelPipeline`.
    ///
    /// - parameters:
    ///     - handler: the `ChannelHandler` to remove.
    ///     - promise: An `EventLoopPromise` that will complete when the `ChannelHandler` is removed.
    public func removeHandler(_ handler: RemovableChannelHandler, promise: EventLoopPromise<Void>?) {
        let contextFuture = self.context(handler: handler).map { context in
            self.removeHandler(context: context, promise: promise)
        }

        contextFuture.cascadeFailure(to: promise)
    }

    /// Remove a `ChannelHandler` from the `ChannelPipeline`.
    ///
    /// - parameters:
    ///     - name: the name that was used to add the `ChannelHandler` to the `ChannelPipeline` before.
    ///     - promise: An `EventLoopPromise` that will complete when the `ChannelHandler` is removed.
    public func removeHandler(name: String, promise: EventLoopPromise<Void>?) {
        let contextFuture = self.context(name: name).map { context in
            self.removeHandler(context: context, promise: promise)
        }

        contextFuture.cascadeFailure(to: promise)
    }

    /// Remove a `ChannelHandler` from the `ChannelPipeline`.
    ///
    /// - parameters:
    ///     - context: the `ChannelHandlerContext` that belongs to `ChannelHandler` that should be removed.
    ///     - promise: An `EventLoopPromise` that will complete when the `ChannelHandler` is removed.
    public func removeHandler(context: ChannelHandlerContext, promise: EventLoopPromise<Void>?) {
        guard context.handler is RemovableChannelHandler else {
            promise?.fail(ChannelError.unremovableHandler)
            return
        }
        func removeHandler0() {
            context.startUserTriggeredRemoval(promise: promise)
        }

        if self.eventLoop.inEventLoop {
            removeHandler0()
        } else {
            self.eventLoop.execute {
                removeHandler0()
            }
        }
    }

    /// Returns the `ChannelHandlerContext` that belongs to a `ChannelHandler`.
    ///
    /// - parameters:
    ///     - handler: the `ChannelHandler` for which the `ChannelHandlerContext` should be returned
    /// - returns: the `EventLoopFuture` which will be notified once the the operation completes.
    public func context(handler: ChannelHandler) -> EventLoopFuture<ChannelHandlerContext> {
        let promise = self.eventLoop.makePromise(of: ChannelHandlerContext.self)

        if self.eventLoop.inEventLoop {
            promise.completeWith(self.contextSync(handler: handler))
        } else {
            self.eventLoop.execute {
                promise.completeWith(self.contextSync(handler: handler))
            }
        }

        return promise.futureResult
    }

    /// Synchronously returns the `ChannelHandlerContext` that belongs to a `ChannelHandler`.
    ///
    /// - Important: This must be called on the `EventLoop`.
    /// - parameters:
    ///     - handler: the `ChannelHandler` for which the `ChannelHandlerContext` should be returned
    /// - returns: the `ChannelHandlerContext` that belongs to the `ChannelHandler`, if one exists.
    fileprivate func contextSync(handler: ChannelHandler) -> Result<ChannelHandlerContext, Error> {
        return self._contextSync({ $0.handler === handler })
    }

    /// Returns the `ChannelHandlerContext` that belongs to a `ChannelHandler`.
    ///
    /// - parameters:
    ///     - name: the name that was used to add the `ChannelHandler` to the `ChannelPipeline` before.
    /// - returns: the `EventLoopFuture` which will be notified once the the operation completes.
    public func context(name: String) -> EventLoopFuture<ChannelHandlerContext> {
        let promise = self.eventLoop.makePromise(of: ChannelHandlerContext.self)

        if self.eventLoop.inEventLoop {
            promise.completeWith(self.contextSync(name: name))
        } else {
            self.eventLoop.execute {
                promise.completeWith(self.contextSync(name: name))
            }
        }

        return promise.futureResult
    }

    /// Synchronously finds and returns the `ChannelHandlerContext` that belongs to the
    /// `ChannelHandler` with the given name.
    ///
    /// - Important: This must be called on the `EventLoop`.
    /// - Parameter name: The name of the `ChannelHandler` to find.
    /// - Returns: the `ChannelHandlerContext` that belongs to the `ChannelHandler`, if one exists.
    fileprivate func contextSync(name: String) -> Result<ChannelHandlerContext, Error> {
        return self._contextSync({ $0.name == name })
    }

    /// Returns the `ChannelHandlerContext` that belongs to a `ChannelHandler` of the given type.
    ///
    /// If multiple channel handlers of the same type are present in the pipeline, returns the context
    /// belonging to the first such handler.
    ///
    /// - parameters:
    ///     - handlerType: The type of the handler to search for.
    /// - returns: the `EventLoopFuture` which will be notified once the the operation completes.
    @inlinable
    public func context<Handler: ChannelHandler>(handlerType: Handler.Type) -> EventLoopFuture<ChannelHandlerContext> {
        let promise = self.eventLoop.makePromise(of: ChannelHandlerContext.self)

        if self.eventLoop.inEventLoop {
            promise.completeWith(self._contextSync(handlerType: handlerType))
        } else {
            self.eventLoop.execute {
                promise.completeWith(self._contextSync(handlerType: handlerType))
            }
        }

        return promise.futureResult
    }

    /// Synchronously finds and returns the `ChannelHandlerContext` that belongs to the first
    /// `ChannelHandler` of the given type.
    ///
    /// - Important: This must be called on the `EventLoop`.
    /// - Parameter handlerType: The type of handler to search for.
    /// - Returns: the `ChannelHandlerContext` that belongs to the `ChannelHandler`, if one exists.
    @inlinable // should be fileprivate
    internal func _contextSync<Handler: ChannelHandler>(handlerType: Handler.Type) -> Result<ChannelHandlerContext, Error> {
        return self._contextSync({ $0.handler is Handler })
    }

    /// Synchronously finds a `ChannelHandlerContext` in the `ChannelPipeline`.
    /// - Important: This must be called on the `EventLoop`.
    @usableFromInline // should be fileprivate
    internal func _contextSync(_ body: @escaping ((ChannelHandlerContext) -> Bool)) -> Result<ChannelHandlerContext, Error> {
        self.eventLoop.assertInEventLoop()

        if let context = self.contextForPredicate0(body) {
            return .success(context)
        } else {
            return .failure(ChannelPipelineError.notFound)
        }
    }

    /// Returns a `ChannelHandlerContext` which matches.
    ///
    /// This skips head and tail (as these are internal and should not be accessible by the user).
    ///
    /// - parameters:
    ///     - body: The predicate to execute per `ChannelHandlerContext` in the `ChannelPipeline`.
    /// - returns: The first `ChannelHandlerContext` that matches or `nil` if none did.
    private func contextForPredicate0(_ body: @escaping((ChannelHandlerContext) -> Bool)) -> ChannelHandlerContext? {
        var curCtx: ChannelHandlerContext? = self.head?.next
        while let context = curCtx, context !== self.tail {
            if body(context) {
                return context
            }
            curCtx = context.next
        }

        return nil
    }

    /// Remove a `ChannelHandlerContext` from the `ChannelPipeline` directly without going through the
    /// `RemovableChannelHandler` API. This must only be used to clear the pipeline on `Channel` tear down and
    /// as a result of the `leavePipeline` call in the `RemovableChannelHandler` API.
    internal func removeHandlerFromPipeline(context: ChannelHandlerContext, promise: EventLoopPromise<Void>?) {
        self.eventLoop.assertInEventLoop()

        let nextCtx = context.next
        let prevCtx = context.prev

        if let prevCtx = prevCtx {
            prevCtx.next = nextCtx
        }
        if let nextCtx = nextCtx {
            nextCtx.prev = prevCtx
        }

        context.invokeHandlerRemoved()
        promise?.succeed(())

        // We need to keep the current node alive until after the callout in case the user uses the context.
        context.next = nil
        context.prev = nil
    }

    /// Returns the next name to use for a `ChannelHandler`.
    private func nextName() -> String {
        self.eventLoop.assertInEventLoop()

        let name = "handler\(idx)"
        idx += 1
        return name
    }

    /// Remove all the `ChannelHandler`s from the `ChannelPipeline` and destroy these.
    ///
    /// This method must only be called from within the `EventLoop`. It should only be called from a `ChannelCore`
    /// implementation. Once called, the `ChannelPipeline` is no longer active and cannot be used again.
    func removeHandlers() {
        self.eventLoop.assertInEventLoop()

        if let head = self.head {
            while let context = head.next {
                removeHandlerFromPipeline(context: context, promise: nil)
            }
            removeHandlerFromPipeline(context: self.head!, promise: nil)
        }
        self.head = nil
        self.tail = nil

        self.destroyed = true
        self._channel = nil
    }

    // Just delegate to the head and tail context
    public func fireChannelRegistered() {
        if eventLoop.inEventLoop {
            fireChannelRegistered0()
        } else {
            eventLoop.execute {
                self.fireChannelRegistered0()
            }
        }
    }

    public func fireChannelUnregistered() {
        if eventLoop.inEventLoop {
            fireChannelUnregistered0()
        } else {
            eventLoop.execute {
                self.fireChannelUnregistered0()
            }
        }
    }

    public func fireChannelInactive() {
        if eventLoop.inEventLoop {
            fireChannelInactive0()
        } else {
            eventLoop.execute {
                self.fireChannelInactive0()
            }
        }
    }

    public func fireChannelActive() {
        if eventLoop.inEventLoop {
            fireChannelActive0()
        } else {
            eventLoop.execute {
                self.fireChannelActive0()
            }
        }
    }

    public func fireChannelRead(_ data: NIOAny) {
        if eventLoop.inEventLoop {
            fireChannelRead0(data)
        } else {
            eventLoop.execute {
                self.fireChannelRead0(data)
            }
        }
    }

    public func fireChannelReadComplete() {
        if eventLoop.inEventLoop {
            fireChannelReadComplete0()
        } else {
            eventLoop.execute {
                self.fireChannelReadComplete0()
            }
        }
    }

    public func fireChannelWritabilityChanged() {
        if eventLoop.inEventLoop {
            fireChannelWritabilityChanged0()
        } else {
            eventLoop.execute {
                self.fireChannelWritabilityChanged0()
            }
        }
    }

    public func fireUserInboundEventTriggered(_ event: Any) {
        if eventLoop.inEventLoop {
            fireUserInboundEventTriggered0(event)
        } else {
            eventLoop.execute {
                self.fireUserInboundEventTriggered0(event)
            }
        }
    }

    public func fireErrorCaught(_ error: Error) {
        if eventLoop.inEventLoop {
            fireErrorCaught0(error: error)
        } else {
            eventLoop.execute {
                self.fireErrorCaught0(error: error)
            }
        }
    }

    public func close(mode: CloseMode = .all, promise: EventLoopPromise<Void>?) {
        if eventLoop.inEventLoop {
            close0(mode: mode, promise: promise)
        } else {
            eventLoop.execute {
                self.close0(mode: mode, promise: promise)
            }
        }
    }

    public func flush() {
        if eventLoop.inEventLoop {
            flush0()
        } else {
            eventLoop.execute {
                self.flush0()
            }
        }
    }

    public func read() {
        if eventLoop.inEventLoop {
            read0()
        } else {
            eventLoop.execute {
                self.read0()
            }
        }
    }

    public func write(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
        if eventLoop.inEventLoop {
            write0(data, promise: promise)
        } else {
            eventLoop.execute {
                self.write0(data, promise: promise)
            }
        }
    }

    public func writeAndFlush(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
        if eventLoop.inEventLoop {
            writeAndFlush0(data, promise: promise)
        } else {
            eventLoop.execute {
                self.writeAndFlush0(data, promise: promise)
            }
        }
    }

    public func bind(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
        if eventLoop.inEventLoop {
            bind0(to: address, promise: promise)
        } else {
            eventLoop.execute {
                self.bind0(to: address, promise: promise)
            }
        }
    }

    public func connect(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
        if eventLoop.inEventLoop {
            connect0(to: address, promise: promise)
        } else {
            eventLoop.execute {
                self.connect0(to: address, promise: promise)
            }
        }
    }

    public func register(promise: EventLoopPromise<Void>?) {
        if eventLoop.inEventLoop {
            register0(promise: promise)
        } else {
            eventLoop.execute {
                self.register0(promise: promise)
            }
        }
    }

    public func triggerUserOutboundEvent(_ event: Any, promise: EventLoopPromise<Void>?) {
        if eventLoop.inEventLoop {
            triggerUserOutboundEvent0(event, promise: promise)
        } else {
            eventLoop.execute {
                self.triggerUserOutboundEvent0(event, promise: promise)
            }
        }
    }

    // These methods are expected to only be called from within the EventLoop

    private var firstOutboundCtx: ChannelHandlerContext? {
        return self.tail?.prev
    }

    private var firstInboundCtx: ChannelHandlerContext? {
        return self.head?.next
    }

    func close0(mode: CloseMode, promise: EventLoopPromise<Void>?) {
        if let firstOutboundCtx = firstOutboundCtx {
            firstOutboundCtx.invokeClose(mode: mode, promise: promise)
        } else {
            promise?.fail(ChannelError.alreadyClosed)
        }
    }

    func flush0() {
        if let firstOutboundCtx = firstOutboundCtx {
            firstOutboundCtx.invokeFlush()
        }
    }

    func read0() {
        if let firstOutboundCtx = firstOutboundCtx {
            firstOutboundCtx.invokeRead()
        }
    }

    func write0(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
        if let firstOutboundCtx = firstOutboundCtx {
            firstOutboundCtx.invokeWrite(data, promise: promise)
        } else {
            promise?.fail(ChannelError.ioOnClosedChannel)
        }
    }

    func writeAndFlush0(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
        if let firstOutboundCtx = firstOutboundCtx {
            firstOutboundCtx.invokeWriteAndFlush(data, promise: promise)
        } else {
            promise?.fail(ChannelError.ioOnClosedChannel)
        }
    }

    func bind0(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
        if let firstOutboundCtx = firstOutboundCtx {
            firstOutboundCtx.invokeBind(to: address, promise: promise)
        } else {
            promise?.fail(ChannelError.ioOnClosedChannel)
        }
    }

    func connect0(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
        if let firstOutboundCtx = firstOutboundCtx {
            firstOutboundCtx.invokeConnect(to: address, promise: promise)
        } else {
            promise?.fail(ChannelError.ioOnClosedChannel)
        }
    }

    func register0(promise: EventLoopPromise<Void>?) {
        if let firstOutboundCtx = firstOutboundCtx {
            firstOutboundCtx.invokeRegister(promise: promise)
        } else {
            promise?.fail(ChannelError.ioOnClosedChannel)
        }
    }

    func triggerUserOutboundEvent0(_ event: Any, promise: EventLoopPromise<Void>?) {
        if let firstOutboundCtx = firstOutboundCtx {
            firstOutboundCtx.invokeTriggerUserOutboundEvent(event, promise: promise)
        } else {
            promise?.fail(ChannelError.ioOnClosedChannel)
        }
    }

    func fireChannelRegistered0() {
        if let firstInboundCtx = firstInboundCtx {
            firstInboundCtx.invokeChannelRegistered()
        }
    }

    func fireChannelUnregistered0() {
        if let firstInboundCtx = firstInboundCtx {
            firstInboundCtx.invokeChannelUnregistered()
        }
    }

    func fireChannelInactive0() {
        if let firstInboundCtx = firstInboundCtx {
            firstInboundCtx.invokeChannelInactive()
        }
    }

    func fireChannelActive0() {
        if let firstInboundCtx = firstInboundCtx {
            firstInboundCtx.invokeChannelActive()
        }
    }

    func fireChannelRead0(_ data: NIOAny) {
        if let firstInboundCtx = firstInboundCtx {
            firstInboundCtx.invokeChannelRead(data)
        }
    }

    func fireChannelReadComplete0() {
        if let firstInboundCtx = firstInboundCtx {
            firstInboundCtx.invokeChannelReadComplete()
        }
    }

    func fireChannelWritabilityChanged0() {
        if let firstInboundCtx = firstInboundCtx {
            firstInboundCtx.invokeChannelWritabilityChanged()
        }
    }

    func fireUserInboundEventTriggered0(_ event: Any) {
        if let firstInboundCtx = firstInboundCtx {
            firstInboundCtx.invokeUserInboundEventTriggered(event)
        }
    }

    func fireErrorCaught0(error: Error) {
        assert((error as? ChannelError).map { $0 != .eof } ?? true)
        if let firstInboundCtx = firstInboundCtx {
            firstInboundCtx.invokeErrorCaught(error)
        }
    }

    private var inEventLoop: Bool {
        return eventLoop.inEventLoop
    }

    /// Create `ChannelPipeline` for a given `Channel`. This method should never be called by the end-user
    /// directly: it is only intended for use with custom `Channel` implementations. Users should always use
    /// `channel.pipeline` to access the `ChannelPipeline` for a `Channel`.
    ///
    /// - parameters:
    ///    - channel: The `Channel` this `ChannelPipeline` is created for.
    public init(channel: Channel) {
        self._channel = channel
        self.eventLoop = channel.eventLoop
        self.head = nil // we need to initialise these to `nil` so we can use `self` in the lines below
        self.tail = nil // we need to initialise these to `nil` so we can use `self` in the lines below

        self.head = ChannelHandlerContext(name: HeadChannelHandler.name, handler: HeadChannelHandler.sharedInstance, pipeline: self)
        self.tail = ChannelHandlerContext(name: TailChannelHandler.name, handler: TailChannelHandler.sharedInstance, pipeline: self)
        self.head?.next = self.tail
        self.tail?.prev = self.head
    }
}

extension ChannelPipeline {
    /// Adds the provided channel handlers to the pipeline in the order given, taking account
    /// of the behaviour of `ChannelHandler.add(first:)`.
    ///
    /// - parameters:
    ///     - handlers: The array of `ChannelHandler`s to be added.
    ///     - position: The position in the `ChannelPipeline` to add `handlers`. Defaults to `.last`.
    ///
    /// - returns: A future that will be completed when all of the supplied `ChannelHandler`s were added.
    public func addHandlers(_ handlers: [ChannelHandler],
                            position: ChannelPipeline.Position = .last) -> EventLoopFuture<Void> {
        let future: EventLoopFuture<Void>

        if self.eventLoop.inEventLoop {
            future = self.eventLoop.makeCompletedFuture(self.addHandlersSync(handlers, position: position))
        } else {
            future = self.eventLoop.submit {
                try self.addHandlersSync(handlers, position: position).get()
            }
        }

        return future
    }

    /// Adds the provided channel handlers to the pipeline in the order given, taking account
    /// of the behaviour of `ChannelHandler.add(first:)`.
    ///
    /// - parameters:
    ///     - handlers: One or more `ChannelHandler`s to be added.
    ///     - position: The position in the `ChannelPipeline` to add `handlers`. Defaults to `.last`.
    ///
    /// - returns: A future that will be completed when all of the supplied `ChannelHandler`s were added.
    public func addHandlers(_ handlers: ChannelHandler...,
                            position: ChannelPipeline.Position = .last) -> EventLoopFuture<Void> {
        return self.addHandlers(handlers, position: position)
    }

    /// Synchronously adds the provided `ChannelHandler`s to the pipeline in the order given, taking
    /// account of the behaviour of `ChannelHandler.add(first:)`.
    ///
    /// - Important: Must be called on the `EventLoop`.
    /// - Parameters:
    ///   - handlers: The array of `ChannelHandler`s to add.
    ///   - position: The position in the `ChannelPipeline` to add the handlers.
    /// - Returns: A result representing whether the handlers were added or not.
    fileprivate func addHandlersSync(_ handlers: [ChannelHandler],
                                     position: ChannelPipeline.Position) -> Result<Void, Error> {
        switch position {
        case .first, .after:
            return self._addHandlersSync(handlers.reversed(), position: position)
        case .last, .before:
            return self._addHandlersSync(handlers, position: position)
        }
    }

    /// Synchronously adds a sequence of `ChannelHandlers` to the pipeline at the given position.
    ///
    /// - Important: Must be called on the `EventLoop`.
    /// - Parameters:
    ///   - handlers: A sequence of handlers to add.
    ///   - position: The position in the `ChannelPipeline` to add the handlers.
    /// - Returns: A result representing whether the handlers were added or not.
    private func _addHandlersSync<Handlers: Sequence>(_ handlers: Handlers,
                                                      position: ChannelPipeline.Position) -> Result<Void, Error> where Handlers.Element == ChannelHandler {
        self.eventLoop.assertInEventLoop()

        for handler in handlers {
            let result = self.addHandlerSync(handler, position: position)
            switch result {
            case .success:
                ()
            case .failure:
                return result
            }
        }

        return .success(())
    }
}

// MARK: - Synchronous View

extension ChannelPipeline {
    /// A view of a `ChannelPipeline` which may be used to invoke synchronous operations.
    ///
    /// All functions **must** be called from the pipeline's event loop.
    public struct SynchronousOperations {
        @usableFromInline
        internal let _pipeline: ChannelPipeline

        fileprivate init(pipeline: ChannelPipeline) {
            self._pipeline = pipeline
        }

        /// The `EventLoop` of the `Channel` this synchronous operations view corresponds to.
        public var eventLoop: EventLoop {
            return self._pipeline.eventLoop
        }

        /// Add a handler to the pipeline.
        ///
        /// - Important: This *must* be called on the event loop.
        /// - Parameters:
        ///   - handler: The handler to add.
        ///   - name: The name to use for the `ChannelHandler` when it's added. If no name is specified the one will be generated.
        ///   - position: The position in the `ChannelPipeline` to add `handler`. Defaults to `.last`.
        public func addHandler(_ handler: ChannelHandler,
                               name: String? = nil,
                               position: ChannelPipeline.Position = .last) throws {
            try self._pipeline.addHandlerSync(handler, name: name, position: position).get()
        }

        /// Add an array of handlers to the pipeline.
        ///
        /// - Important: This *must* be called on the event loop.
        /// - Parameters:
        ///   - handlers: The handlers to add.
        ///   - position: The position in the `ChannelPipeline` to add `handlers`. Defaults to `.last`.
        public func addHandlers(_ handlers: [ChannelHandler],
                                position: ChannelPipeline.Position = .last) throws {
            try self._pipeline.addHandlersSync(handlers, position: position).get()
        }

        /// Add one or more handlers to the pipeline.
        ///
        /// - Important: This *must* be called on the event loop.
        /// - Parameters:
        ///   - handlers: The handlers to add.
        ///   - position: The position in the `ChannelPipeline` to add `handlers`. Defaults to `.last`.
        public func addHandlers(_ handlers: ChannelHandler...,
                                position: ChannelPipeline.Position = .last) throws {
            try self._pipeline.addHandlersSync(handlers, position: position).get()
        }

        /// Returns the `ChannelHandlerContext` for the given handler instance if it is in
        /// the `ChannelPipeline`, if it exists.
        ///
        /// - Important: This *must* be called on the event loop.
        /// - Parameter handler: The handler belonging to the context to fetch.
        /// - Returns: The `ChannelHandlerContext` associated with the handler.
        public func context(handler: ChannelHandler) throws -> ChannelHandlerContext {
            return try self._pipeline._contextSync({ $0.handler === handler }).get()
        }

        /// Returns the `ChannelHandlerContext` for the handler with the given name, if one exists.
        ///
        /// - Important: This *must* be called on the event loop.
        /// - Parameter name: The name of the handler whose context is being fetched.
        /// - Returns: The `ChannelHandlerContext` associated with the handler.
        public func context(name: String) throws -> ChannelHandlerContext {
            return try self._pipeline.contextSync(name: name).get()
        }

        /// Returns the `ChannelHandlerContext` for the handler of given type, if one exists.
        ///
        /// - Important: This *must* be called on the event loop.
        /// - Parameter name: The name of the handler whose context is being fetched.
        /// - Returns: The `ChannelHandlerContext` associated with the handler.
        @inlinable
        public func context<Handler: ChannelHandler>(handlerType: Handler.Type) throws -> ChannelHandlerContext {
            return try self._pipeline._contextSync(handlerType: handlerType).get()
        }

        /// Returns the `ChannelHandler` of the given type from the `ChannelPipeline`, if it exists.
        ///
        /// - Important: This *must* be called on the event loop.
        /// - Returns: A `ChannelHandler` of the given type if one exists in the `ChannelPipeline`.
        @inlinable
        public func handler<Handler: ChannelHandler>(type _: Handler.Type) throws -> Handler {
            return try self._pipeline._handlerSync(type: Handler.self).get()
        }
    }

    /// Returns a view of operations which can be performed synchronously on this pipeline. All
    /// operations **must** be called on the event loop.
    public var syncOperations: SynchronousOperations {
        return SynchronousOperations(pipeline: self)
    }
}

extension ChannelPipeline {
    /// A `Position` within the `ChannelPipeline` used to insert handlers into the `ChannelPipeline`.
    public enum Position {
        /// The first `ChannelHandler` -- the front of the `ChannelPipeline`.
        case first

        /// The last `ChannelHandler` -- the back of the `ChannelPipeline`.
        case last

        /// Before the given `ChannelHandler`.
        case before(ChannelHandler)

        /// After the given `ChannelHandler`.
        case after(ChannelHandler)
    }
}

/// Special `ChannelHandler` that forwards all events to the `Channel.Unsafe` implementation.
/* private but tests */ final class HeadChannelHandler: _ChannelOutboundHandler {

    static let name = "head"
    static let sharedInstance = HeadChannelHandler()

    private init() { }

    func register(context: ChannelHandlerContext, promise: EventLoopPromise<Void>?) {
        context.channel._channelCore.register0(promise: promise)
    }

    func bind(context: ChannelHandlerContext, to address: SocketAddress, promise: EventLoopPromise<Void>?) {
        context.channel._channelCore.bind0(to: address, promise: promise)
    }

    func connect(context: ChannelHandlerContext, to address: SocketAddress, promise: EventLoopPromise<Void>?) {
        context.channel._channelCore.connect0(to: address, promise: promise)
    }

    func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) {
        context.channel._channelCore.write0(data, promise: promise)
    }

    func flush(context: ChannelHandlerContext) {
        context.channel._channelCore.flush0()
    }

    func close(context: ChannelHandlerContext, mode: CloseMode, promise: EventLoopPromise<Void>?) {
        context.channel._channelCore.close0(error: mode.error, mode: mode, promise: promise)
    }

    func read(context: ChannelHandlerContext) {
        context.channel._channelCore.read0()
    }

    func triggerUserOutboundEvent(context: ChannelHandlerContext, event: Any, promise: EventLoopPromise<Void>?) {
        context.channel._channelCore.triggerUserOutboundEvent0(event, promise: promise)
    }

}

private extension CloseMode {
    /// Returns the error to fail outstanding operations writes with.
    var error: ChannelError {
        switch self {
        case .all:
            return .ioOnClosedChannel
        case .output:
            return .outputClosed
        case .input:
            return .inputClosed
        }
    }
}

/// Special `ChannelInboundHandler` which will consume all inbound events.
/* private but tests */ final class TailChannelHandler: _ChannelInboundHandler {

    static let name = "tail"
    static let sharedInstance = TailChannelHandler()

    private init() { }

    func channelRegistered(context: ChannelHandlerContext) {
        // Discard
    }

    func channelUnregistered(context: ChannelHandlerContext) {
        // Discard
    }

    func channelActive(context: ChannelHandlerContext) {
        // Discard
    }

    func channelInactive(context: ChannelHandlerContext) {
        // Discard
    }

    func channelReadComplete(context: ChannelHandlerContext) {
        // Discard
    }

    func channelWritabilityChanged(context: ChannelHandlerContext) {
        // Discard
    }

    func userInboundEventTriggered(context: ChannelHandlerContext, event: Any) {
        // Discard
    }

    func errorCaught(context: ChannelHandlerContext, error: Error) {
        context.channel._channelCore.errorCaught0(error: error)
    }

    func channelRead(context: ChannelHandlerContext, data: NIOAny) {
        context.channel._channelCore.channelRead0(data)
    }
}

/// `Error` that is used by the `ChannelPipeline` to inform the user of an error.
public enum ChannelPipelineError: Error {
    /// `ChannelHandler` was already removed.
    case alreadyRemoved
    /// `ChannelHandler` was not found.
    case notFound
}

/// Every `ChannelHandler` has -- when added to a `ChannelPipeline` -- a corresponding `ChannelHandlerContext` which is
/// the way `ChannelHandler`s can interact with other `ChannelHandler`s in the pipeline.
///
/// Most `ChannelHandler`s need to send events through the `ChannelPipeline` which they do by calling the respective
/// method on their `ChannelHandlerContext`. In fact all the `ChannelHandler` default implementations just forward
/// the event using the `ChannelHandlerContext`.
///
/// Many events are instrumental for a `ChannelHandler`'s life-cycle and it is therefore very important to send them
/// at the right point in time. Often, the right behaviour is to react to an event and then forward it to the next
/// `ChannelHandler`.
public final class ChannelHandlerContext: ChannelInvoker {
    // visible for ChannelPipeline to modify
    fileprivate var next: Optional<ChannelHandlerContext>
    fileprivate var prev: Optional<ChannelHandlerContext>

    public let pipeline: ChannelPipeline

    public var channel: Channel {
        return self.pipeline.channel
    }

    public var handler: ChannelHandler {
        return self.inboundHandler ?? self.outboundHandler!
    }

    public var remoteAddress: SocketAddress? {
        do {
            // Fast-path access to the remoteAddress.
            return try self.channel._channelCore.remoteAddress0()
        } catch ChannelError.ioOnClosedChannel {
            // Channel was closed already but we may still have the address cached so try to access it via the Channel
            // so we are able to use it in channelInactive(...) / handlerRemoved(...) methods.
            return self.channel.remoteAddress
        } catch {
            return nil
        }
    }

    public var localAddress: SocketAddress? {
        do {
            // Fast-path access to the localAddress.
            return try self.channel._channelCore.localAddress0()
        } catch ChannelError.ioOnClosedChannel {
            // Channel was closed already but we may still have the address cached so try to access it via the Channel
            // so we are able to use it in channelInactive(...) / handlerRemoved(...) methods.
            return self.channel.localAddress
        } catch {
            return nil
        }
    }

    public var eventLoop: EventLoop {
        return self.pipeline.eventLoop
    }

    public let name: String
    private let inboundHandler: _ChannelInboundHandler?
    private let outboundHandler: _ChannelOutboundHandler?
    private var removeHandlerInvoked = false
    private var userTriggeredRemovalStarted = false

    // Only created from within ChannelPipeline
    fileprivate init(name: String, handler: ChannelHandler, pipeline: ChannelPipeline) {
        self.name = name
        self.pipeline = pipeline
        self.inboundHandler = handler as? _ChannelInboundHandler
        self.outboundHandler = handler as? _ChannelOutboundHandler
        self.next = nil
        self.prev = nil
        precondition(self.inboundHandler != nil || self.outboundHandler != nil, "ChannelHandlers need to either be inbound or outbound")
    }

    /// Send a `channelRegistered` event to the next (inbound) `ChannelHandler` in the `ChannelPipeline`.
    ///
    /// - note: For correct operation it is very important to forward any `channelRegistered` event using this method at the right point in time, that is usually when received.
    public func fireChannelRegistered() {
        self.next?.invokeChannelRegistered()
    }

    /// Send a `channelUnregistered` event to the next (inbound) `ChannelHandler` in the `ChannelPipeline`.
    ///
    /// - note: For correct operation it is very important to forward any `channelUnregistered` event using this method at the right point in time, that is usually when received.
    public func fireChannelUnregistered() {
        self.next?.invokeChannelUnregistered()
    }

    /// Send a `channelActive` event to the next (inbound) `ChannelHandler` in the `ChannelPipeline`.
    ///
    /// - note: For correct operation it is very important to forward any `channelActive` event using this method at the right point in time, that is often when received.
    public func fireChannelActive() {
        self.next?.invokeChannelActive()
    }

    /// Send a `channelInactive` event to the next (inbound) `ChannelHandler` in the `ChannelPipeline`.
    ///
    /// - note: For correct operation it is very important to forward any `channelInactive` event using this method at the right point in time, that is often when received.
    public func fireChannelInactive() {
        self.next?.invokeChannelInactive()
    }

    /// Send data to the next inbound `ChannelHandler`. The data should be of type `ChannelInboundHandler.InboundOut`.
    public func fireChannelRead(_ data: NIOAny) {
        self.next?.invokeChannelRead(data)
    }

    /// Signal to the next `ChannelHandler` that a read burst has finished.
    public func fireChannelReadComplete() {
        self.next?.invokeChannelReadComplete()
    }

    /// Send a `writabilityChanged` event to the next (inbound) `ChannelHandler` in the `ChannelPipeline`.
    ///
    /// - note: For correct operation it is very important to forward any `writabilityChanged` event using this method at the right point in time, that is usually when received.
    public func fireChannelWritabilityChanged() {
        self.next?.invokeChannelWritabilityChanged()
    }

    /// Send an error to the next inbound `ChannelHandler`.
    public func fireErrorCaught(_ error: Error) {
        self.next?.invokeErrorCaught(error)
    }

    /// Send a user event to the next inbound `ChannelHandler`.
    public func fireUserInboundEventTriggered(_ event: Any) {
        self.next?.invokeUserInboundEventTriggered(event)
    }

    /// Send a `register` event to the next (outbound) `ChannelHandler` in the `ChannelPipeline`.
    ///
    /// - note: For correct operation it is very important to forward any `register` event using this method at the right point in time, that is usually when received.
    public func register(promise: EventLoopPromise<Void>?) {
        if let outboundNext = self.prev {
            outboundNext.invokeRegister(promise: promise)
        } else {
            promise?.fail(ChannelError.ioOnClosedChannel)
        }
    }

    /// Send a `bind` event to the next outbound `ChannelHandler` in the `ChannelPipeline`.
    /// When the `bind` event reaches the `HeadChannelHandler` a `ServerSocketChannel` will be bound.
    ///
    /// - parameters:
    ///     - address: The address to bind to.
    ///     - promise: The promise fulfilled when the socket is bound or failed if it cannot be bound.
    public func bind(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
        if let outboundNext = self.prev {
            outboundNext.invokeBind(to: address, promise: promise)
        } else {
            promise?.fail(ChannelError.ioOnClosedChannel)
        }
    }

    /// Send a `connect` event to the next outbound `ChannelHandler` in the `ChannelPipeline`.
    /// When the `connect` event reaches the `HeadChannelHandler` a `SocketChannel` will be connected.
    ///
    /// - parameters:
    ///     - address: The address to connect to.
    ///     - promise: The promise fulfilled when the socket is connected or failed if it cannot be connected.
    public func connect(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
        if let outboundNext = self.prev {
            outboundNext.invokeConnect(to: address, promise: promise)
        } else {
            promise?.fail(ChannelError.ioOnClosedChannel)
        }
    }

    /// Send a `write` event to the next outbound `ChannelHandler` in the `ChannelPipeline`.
    /// When the `write` event reaches the `HeadChannelHandler` the data will be enqueued to be written on the next
    /// `flush` event.
    ///
    /// - parameters:
    ///     - data: The data to write, should be of type `ChannelOutboundHandler.OutboundOut`.
    ///     - promise: The promise fulfilled when the data has been written or failed if it cannot be written.
    public func write(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
        if let outboundNext = self.prev {
            outboundNext.invokeWrite(data, promise: promise)
        } else {
            promise?.fail(ChannelError.ioOnClosedChannel)
        }
    }

    /// Send a `flush` event to the next outbound `ChannelHandler` in the `ChannelPipeline`.
    /// When the `flush` event reaches the `HeadChannelHandler` the data previously enqueued will be attempted to be
    /// written to the socket.
    ///
    /// - parameters:
    ///     - promise: The promise fulfilled when the previously written data been flushed or failed if it cannot be flushed.
    public func flush() {
        if let outboundNext = self.prev {
            outboundNext.invokeFlush()
        }
    }

    /// Send a `write` event followed by a `flush` event to the next outbound `ChannelHandler` in the `ChannelPipeline`.
    /// When the `write` event reaches the `HeadChannelHandler` the data will be enqueued to be written when the `flush`
    /// also reaches the `HeadChannelHandler`.
    ///
    /// - parameters:
    ///     - data: The data to write, should be of type `ChannelOutboundHandler.OutboundOut`.
    ///     - promise: The promise fulfilled when the previously written data been written and flushed or if that failed.
    public func writeAndFlush(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
        if let outboundNext = self.prev {
            outboundNext.invokeWriteAndFlush(data, promise: promise)
        } else {
            promise?.fail(ChannelError.ioOnClosedChannel)
        }
    }

    /// Send a `read` event to the next outbound `ChannelHandler` in the `ChannelPipeline`.
    /// When the `read` event reaches the `HeadChannelHandler` the interest to read data will be signalled to the
    /// `Selector`. This will subsequently -- when data becomes readable -- cause `channelRead` events containing the
    /// data being sent through the `ChannelPipeline`.
    public func read() {
        if let outboundNext = self.prev {
            outboundNext.invokeRead()
        }
    }

    /// Send a `close` event to the next outbound `ChannelHandler` in the `ChannelPipeline`.
    /// When the `close` event reaches the `HeadChannelHandler` the socket will be closed.
    ///
    /// - parameters:
    ///     - mode: The `CloseMode` to use.
    ///     - promise: The promise fulfilled when the `Channel` has been closed or failed if it the closing failed.
    public func close(mode: CloseMode = .all, promise: EventLoopPromise<Void>?) {
        if let outboundNext = self.prev {
            outboundNext.invokeClose(mode: mode, promise: promise)
        } else {
            promise?.fail(ChannelError.alreadyClosed)
        }
    }

    /// Send a user event to the next outbound `ChannelHandler` in the `ChannelPipeline`.
    ///
    /// - parameters:
    ///     - event: The user event to send.
    ///     - promise: The promise fulfilled when the user event has been sent or failed if it couldn't be sent.
    public func triggerUserOutboundEvent(_ event: Any, promise: EventLoopPromise<Void>?) {
        if let outboundNext = self.prev {
            outboundNext.invokeTriggerUserOutboundEvent(event, promise: promise)
        } else {
            promise?.fail(ChannelError.ioOnClosedChannel)
        }
    }

    fileprivate func invokeChannelRegistered() {
        self.eventLoop.assertInEventLoop()

        if let inboundHandler = self.inboundHandler {
            inboundHandler.channelRegistered(context: self)
        } else {
            self.next?.invokeChannelRegistered()
        }
    }

    fileprivate func invokeChannelUnregistered() {
        self.eventLoop.assertInEventLoop()

        if let inboundHandler = self.inboundHandler {
            inboundHandler.channelUnregistered(context: self)
        } else {
            self.next?.invokeChannelUnregistered()
        }
    }

    fileprivate func invokeChannelActive() {
        self.eventLoop.assertInEventLoop()

        if let inboundHandler = self.inboundHandler {
            inboundHandler.channelActive(context: self)
        } else {
            self.next?.invokeChannelActive()
        }
    }

    fileprivate func invokeChannelInactive() {
        self.eventLoop.assertInEventLoop()

        if let inboundHandler = self.inboundHandler {
            inboundHandler.channelInactive(context: self)
        } else {
            self.next?.invokeChannelInactive()
        }
    }

    fileprivate func invokeChannelRead(_ data: NIOAny) {
        self.eventLoop.assertInEventLoop()

        if let inboundHandler = self.inboundHandler {
            inboundHandler.channelRead(context: self, data: data)
        } else {
            self.next?.invokeChannelRead(data)
        }
    }

    fileprivate func invokeChannelReadComplete() {
        self.eventLoop.assertInEventLoop()

        if let inboundHandler = self.inboundHandler {
            inboundHandler.channelReadComplete(context: self)
        } else {
            self.next?.invokeChannelReadComplete()
        }
    }

    fileprivate func invokeChannelWritabilityChanged() {
        self.eventLoop.assertInEventLoop()

        if let inboundHandler = self.inboundHandler {
            inboundHandler.channelWritabilityChanged(context: self)
        } else {
            self.next?.invokeChannelWritabilityChanged()
        }
    }

    fileprivate func invokeErrorCaught(_ error: Error) {
        self.eventLoop.assertInEventLoop()

        if let inboundHandler = self.inboundHandler {
            inboundHandler.errorCaught(context: self, error: error)
        } else {
            self.next?.invokeErrorCaught(error)
        }
    }

    fileprivate func invokeUserInboundEventTriggered(_ event: Any) {
        self.eventLoop.assertInEventLoop()

        if let inboundHandler = self.inboundHandler {
            inboundHandler.userInboundEventTriggered(context: self, event: event)
        } else {
            self.next?.invokeUserInboundEventTriggered(event)
        }
    }

    fileprivate func invokeRegister(promise: EventLoopPromise<Void>?) {
        self.eventLoop.assertInEventLoop()

        if let outboundHandler = self.outboundHandler {
            outboundHandler.register(context: self, promise: promise)
        } else {
            self.prev?.invokeRegister(promise: promise)
        }
    }

   fileprivate func invokeBind(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
        self.eventLoop.assertInEventLoop()

        if let outboundHandler = self.outboundHandler {
            outboundHandler.bind(context: self, to: address, promise: promise)
        } else {
            self.prev?.invokeBind(to: address, promise: promise)
        }
    }

    fileprivate func invokeConnect(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
        self.eventLoop.assertInEventLoop()

        if let outboundHandler = self.outboundHandler {
            outboundHandler.connect(context: self, to: address, promise: promise)
        } else {
            self.prev?.invokeConnect(to: address, promise: promise)
        }
    }

    fileprivate func invokeWrite(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
        self.eventLoop.assertInEventLoop()

        if let outboundHandler = self.outboundHandler {
            outboundHandler.write(context: self, data: data, promise: promise)
        } else {
            self.prev?.invokeWrite(data, promise: promise)
        }
    }

    fileprivate func invokeFlush() {
        self.eventLoop.assertInEventLoop()

        if let outboundHandler = self.outboundHandler {
            outboundHandler.flush(context: self)
        } else {
            self.prev?.invokeFlush()
        }
    }

    fileprivate func invokeWriteAndFlush(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
        self.eventLoop.assertInEventLoop()

        if let outboundHandler = self.outboundHandler {
            outboundHandler.write(context: self, data: data, promise: promise)
            outboundHandler.flush(context: self)
        } else {
            self.prev?.invokeWriteAndFlush(data, promise: promise)
        }
    }

    fileprivate func invokeRead() {
        self.eventLoop.assertInEventLoop()

        if let outboundHandler = self.outboundHandler {
            outboundHandler.read(context: self)
        } else {
            self.prev?.invokeRead()
        }
    }

    fileprivate func invokeClose(mode: CloseMode, promise: EventLoopPromise<Void>?) {
        self.eventLoop.assertInEventLoop()

        if let outboundHandler = self.outboundHandler {
            outboundHandler.close(context: self, mode: mode, promise: promise)
        } else {
            self.prev?.invokeClose(mode: mode, promise: promise)
        }
    }

    fileprivate func invokeTriggerUserOutboundEvent(_ event: Any, promise: EventLoopPromise<Void>?) {
        self.eventLoop.assertInEventLoop()

        if let outboundHandler = self.outboundHandler {
            outboundHandler.triggerUserOutboundEvent(context: self, event: event, promise: promise)
        } else {
            self.prev?.invokeTriggerUserOutboundEvent(event, promise: promise)
        }
    }

    fileprivate func invokeHandlerAdded() {
        self.eventLoop.assertInEventLoop()

        handler.handlerAdded(context: self)
    }

    fileprivate func invokeHandlerRemoved() {
        self.eventLoop.assertInEventLoop()
        guard !self.removeHandlerInvoked else {
            return
        }
        self.removeHandlerInvoked = true

        handler.handlerRemoved(context: self)
    }
}

extension ChannelHandlerContext {
    /// A `RemovalToken` is handed to a `RemovableChannelHandler` when its `removeHandler` function is invoked. A
    /// `RemovableChannelHandler` is then required to remove itself from the `ChannelPipeline`. The removal process
    /// is finalized by handing the `RemovalToken` to the `ChannelHandlerContext.leavePipeline` function.
    public struct RemovalToken {
        internal let promise: EventLoopPromise<Void>?
    }

    /// Synchronously remove the `ChannelHandler` with the given `ChannelHandlerContext`.
    ///
    /// - note: This function must only be used from a `RemovableChannelHandler` to remove itself. Calling this method
    ///         on any other `ChannelHandlerContext` leads to undefined behaviour.
    ///
    /// - parameters:
    ///    - removalToken: The removal token received from `RemovableChannelHandler.removeHandler`
    public func leavePipeline(removalToken: RemovalToken) {
        self.eventLoop.preconditionInEventLoop()
        self.pipeline.removeHandlerFromPipeline(context: self, promise: removalToken.promise)
    }

    internal func startUserTriggeredRemoval(promise: EventLoopPromise<Void>?) {
        self.eventLoop.assertInEventLoop()
        guard !self.userTriggeredRemovalStarted else {
            promise?.fail(NIOAttemptedToRemoveHandlerMultipleTimesError())
            return
        }
        self.userTriggeredRemovalStarted = true
        (self.handler as! RemovableChannelHandler).removeHandler(context: self,
                                                                 removalToken: .init(promise: promise))
    }
}

extension ChannelPipeline: CustomDebugStringConvertible {
    public var debugDescription: String {
        // This method forms output in the following format:
        //
        // ChannelPipeline[0x0000000000000000]:
        //                      [I] ↓↑ [O]
        //  <incoming handler type> ↓↑                         [<name>]
        //                          ↓↑ <outgoing handler type> [<name>]
        //    <duplex handler type> ↓↑ <duplex handler type>   [<name>]
        //
        var desc = ["ChannelPipeline[\(ObjectIdentifier(self))]:"]
        let debugInfos = self.collectHandlerDebugInfos()
        let maxIncomingTypeNameCount = debugInfos.filter { $0.isIncoming }
            .map { $0.typeName.count }
            .max() ?? 0
        let maxOutgoingTypeNameCount = debugInfos.filter { $0.isOutgoing }
            .map { $0.typeName.count }
            .max() ?? 0

        func whitespace(count: Int) -> String {
            return String(repeating: " ", count: count)
        }

        if debugInfos.isEmpty {
            desc.append(" <no handlers>")
        } else {
            desc.append(whitespace(count: maxIncomingTypeNameCount - 2) + "[I] ↓↑ [O]")
            for debugInfo in debugInfos {
                var line = [String]()
                line.append(" ")
                if debugInfo.isIncoming {
                    line.append(whitespace(count: maxIncomingTypeNameCount - debugInfo.typeName.count))
                    line.append(debugInfo.typeName)
                } else {
                    line.append(whitespace(count: maxIncomingTypeNameCount))
                }
                line.append(" ↓↑ ")
                if debugInfo.isOutgoing {
                    line.append(debugInfo.typeName)
                    line.append(whitespace(count: maxOutgoingTypeNameCount - debugInfo.typeName.count))
                } else {
                    line.append(whitespace(count: maxOutgoingTypeNameCount))
                }
                line.append(" ")
                line.append("[\(debugInfo.name)]")
                desc.append(line.joined())
            }
        }

        return desc.joined(separator: "\n")
    }

    /// Returns the first `ChannelHandler` of the given type.
    ///
    /// - parameters:
    ///     - type: the type of `ChannelHandler` to return.
    @inlinable
    public func handler<Handler: ChannelHandler>(type _: Handler.Type) -> EventLoopFuture<Handler> {
        return self.context(handlerType: Handler.self).map { context in
            guard let typedContext = context.handler as? Handler else {
                preconditionFailure("Expected channel handler of type \(Handler.self), got \(type(of: context.handler)) instead.")
            }

            return typedContext
        }
    }

    /// Synchronously finds and returns the first `ChannelHandler` of the given type.
    ///
    /// - Important: This must be called on the `EventLoop`.
    /// - Parameters:
    ///     - type: the type of `ChannelHandler` to return.
    @inlinable // should be fileprivate
    internal func _handlerSync<Handler: ChannelHandler>(type _: Handler.Type) -> Result<Handler, Error> {
        return self._contextSync(handlerType: Handler.self).map { context in
            guard let typedContext = context.handler as? Handler else {
                preconditionFailure("Expected channel handler of type \(Handler.self), got \(type(of: context.handler)) instead.")
            }
            return typedContext
        }
    }

    private struct ChannelHandlerDebugInfo {
        let handler: ChannelHandler
        let name: String
        var isIncoming: Bool {
            return self.handler is _ChannelInboundHandler
        }
        var isOutgoing: Bool {
            return self.handler is _ChannelOutboundHandler
        }
        var typeName: String {
            return "\(type(of: self.handler))"
        }
    }

    private func collectHandlerDebugInfos() -> [ChannelHandlerDebugInfo] {
        var handlers = [ChannelHandlerDebugInfo]()
        var node = self.head?.next
        while let context = node, context !== self.tail {
            handlers.append(.init(handler: context.handler, name: context.name))
            node = context.next
        }
        return handlers
    }
}
